package com.koron.standard.riskSources.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.koron.bean.util.RedisUtils;
import com.koron.common.core.business.configsettings.utils.ConfigInfoTool;
import com.koron.standard.bean.base.BaseBean;
import com.koron.standard.bean.query.PageQuery;
import com.koron.standard.riskSources.bean.dto.RiskExcel;
import com.koron.standard.riskSources.bean.po.RiskItem;
import com.koron.standard.riskSources.bean.po.RiskSources;
import com.koron.standard.riskSources.bean.query.RiskSourcesQuery;
import com.koron.standard.riskSources.mapper.RiskSourcesMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 作业风险源(RiskSources)表服务实现类
 *
 * @author jzp
 * @since 2021-09-14
 */
@Service
@Slf4j
public class RiskSourcesService extends ServiceImpl<RiskSourcesMapper, RiskSources> {

    @Autowired
    private RiskItemService riskItemService;

    private String getApi() {
        Map<String, String> map = ConfigInfoTool.getConfigByCode("standard", "risk_source_config");
        String api = map.get("api");
        if (StrUtil.isEmpty(api)) {
            api = "https://safety.gdhwater.com/hazardSource/list.htm";
        }
        return api;
    }

    public String getIdByTypeName(RiskSources riskSources) {
        LambdaQueryWrapper<RiskSources> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RiskSources::getFirstTypeName, riskSources.getFirstTypeName());
        wrapper.eq(RiskSources::getSecondTypeName, riskSources.getSecondTypeName());
        wrapper.eq(RiskSources::getThirdTypeName, riskSources.getThirdTypeName());
        wrapper.eq(RiskSources::getFourthTypeName, riskSources.getFourthTypeName());
        RiskSources one = getOne(wrapper, false);
        return one == null ? null : one.getId();
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void saveRisk(JSONArray array) {
        List<RiskSources> rsList = new ArrayList<>();
        List<RiskItem> riList = new ArrayList<>();

        for (Object o : array) {
            JSONObject jsonObject = new JSONObject(o);
            RiskSources riskSources = toRiskSources(jsonObject);
            rsList.add(riskSources);
            JSONArray risks = jsonObject.getJSONArray("risks");
            for (Object risk : risks) {
                JSONObject riskObject = new JSONObject(risk);
                RiskItem riskItem = toRiskItem(riskObject);
                riList.add(riskItem);
            }
        }
        log.info("保存风险源{}条数据", rsList.size());
        this.saveBatch(rsList);
        riskItemService.saveBatch(riList);
    }

    private RiskSources toRiskSources(JSONObject jsonObject) {
        RiskSources riskSources = new RiskSources();

        riskSources.setId(jsonObject.getStr("id"));
        riskSources.setCreateBy(jsonObject.getStr("addUser"));
        riskSources.setCreateByName(jsonObject.getStr("addUserName"));
        riskSources.setCreateTime(new Date(jsonObject.getInt("addTime")));
        riskSources.setUpdateBy(jsonObject.getStr("updateUser"));
        riskSources.setUpdateByName(jsonObject.getStr("updateUserName"));
        riskSources.setUpdateTime(new Date(jsonObject.getInt("updateTime")));
        riskSources.setFirstTypeName(jsonObject.getStr("hazardTypeName"));
        riskSources.setSecondTypeName(jsonObject.getStr("secondElementTypeName"));
        riskSources.setThirdTypeName(jsonObject.getStr("name"));
        riskSources.setFourthTypeName(jsonObject.getStr("fourthElementType"));
        riskSources.setDeleteFlag(0);

        return riskSources;
    }

    private RiskItem toRiskItem(JSONObject jsonObject) {
        RiskItem riskItem = new RiskItem();

        riskItem.setId(jsonObject.getStr("id"));
        riskItem.setCreateTime(new Date(jsonObject.getInt("addTime")));
        riskItem.setSourceId(jsonObject.getStr("hazardSourceId"));
        riskItem.setDesc(jsonObject.getStr("riskInfo"));
        riskItem.setPossible(jsonObject.getInt("possibility"));
        riskItem.setSerious(jsonObject.getInt("severity"));
        riskItem.setMeasures(jsonObject.getStr("precaution"));
        return riskItem;
    }

    /**
     * 删除全表数据
     */
    public void truncateAllData() {
        log.info("清除风险源表数据");
        getBaseMapper().truncateData();
        riskItemService.truncateData();
    }

    public LambdaQueryWrapper<RiskSources> queryWrapper(RiskSourcesQuery query) {
        LambdaQueryWrapper<RiskSources> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(StrUtil.isNotEmpty(query.getFirstTypeName()), RiskSources::getFirstTypeName, query.getFirstTypeName());
        wrapper.eq(StrUtil.isNotEmpty(query.getSecondTypeName()), RiskSources::getSecondTypeName, query.getSecondTypeName());
        wrapper.like(StrUtil.isNotEmpty(query.getThirdTypeName()), RiskSources::getThirdTypeName, query.getThirdTypeName());
        wrapper.like(StrUtil.isNotEmpty(query.getFourthTypeName()), RiskSources::getFourthTypeName, query.getFourthTypeName());
        return wrapper;
    }

    public Page<RiskSources> page(PageQuery<RiskSources> pageQuery, RiskSourcesQuery query) {
        Page<RiskSources> page = pageQuery.convertPage();
        page(page, queryWrapper(query));
        return page;
    }

    public List<RiskExcel> listExcel(RiskSourcesQuery query) {
        List<RiskSources> list = list(queryWrapper(query));
        if (list == null) {
            return new ArrayList<>();
        }
        List<RiskItem> riskItems = riskItemService.listBySourceIds(list.stream().map(BaseBean::getId).collect(Collectors.toList()));
        // <sourceId : riskItems>
        Map<String, List<RiskItem>> collect = riskItems.stream().collect(Collectors.groupingBy(RiskItem::getSourceId));
        return list.stream().flatMap(riskSources -> Optional.ofNullable(collect.get(riskSources.getId()))
                .map(riskItemListNa -> riskItemListNa.stream().map(riskItem -> {
                    RiskExcel riskExcel = BeanUtil.toBean(riskSources, RiskExcel.class);
                    riskExcel.setRiskLevel(riskItem.getRiskLevel());
                    riskExcel.setDesc(riskItem.getDesc());
                    riskExcel.setPossible(riskItem.getPossible());
                    riskExcel.setSerious(riskItem.getSerious());
                    riskExcel.setMeasures(riskItem.getMeasures());
                    return riskExcel;
                })).orElse(Stream.of(BeanUtil.toBean(riskSources, RiskExcel.class)))).collect(Collectors.toList());
    }

    public List<RiskSources> listByParentId(String firstName) {
        LambdaQueryWrapper<RiskSources> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isEmpty(firstName) || "0".equals(firstName)) {
            wrapper.groupBy(RiskSources::getFirstTypeName);
        } else {
            wrapper.eq(RiskSources::getFirstTypeName, firstName);
            wrapper.groupBy(RiskSources::getSecondTypeName);
        }
        return list(wrapper);
    }

    /**
     * 定时任务，每天凌晨执行
     * 自动同步风险源数据
     */
    // @Scheduled(cron = "0 0 0 * * *")
    public void sync() {
        // 0. 探测接口是否可用
        String api = getApi();
        HashMap<String, Object> paramMap = new HashMap<>();
        paramMap.put("pageSize", 1);
        paramMap.put("page", 1);
        HttpResponse execute = HttpRequest.get(api).form(paramMap).timeout(10000).execute();
        // 如果请求失败，直接停止
        if (!execute.isOk()) {
            log.warn("api：{}服务不可用", api);
            throw new HttpException("风险源同步服务不可用");
        }
        // 设置更新时间
        RedisUtils.set("risk_source_sync_time", DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm"));
        // 1. 删除原数据
        truncateAllData();

        // 2. 请求数据
        int page = 1;
        paramMap.put("pageSize", 50);
        log.info("开始同步风险源信息，url:{}", api);
        while (true) {
            log.info("开始爬取{} - {} 条", page * 50 - 50, page * 50);
            paramMap.put("page", page++);
            String result = HttpUtil.get(api, paramMap, 10000);

            // 2. 解析数据
            JSONObject jsonObject = JSONUtil.parseObj(result);
            JSONObject data = jsonObject.getJSONObject("data");
            JSONArray list = data.getJSONArray("list");
            if (CollUtil.isEmpty(list)) {
                break;
            }

            // 3. 保存数据
            saveRisk(list);
        }
        log.info("同步完毕");
    }

    public boolean removeRiskById(Serializable id) {
        LambdaQueryWrapper<RiskItem> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RiskItem::getSourceId, id);
        riskItemService.remove(wrapper);
        return super.removeById(id);
    }

}
